/**
 * plUnit - Prolog Unit Testing
 */

alike1( [], _ ).
alike1( [A|R], B ) :- member( A, B ), alike1( R, B ).

% Compara 2 colecciones a ver si tienen los mismos elementos
% No importa el orden y se pueden repetir distinto
alike( A, B ) :- alike1( A, B ), alike1( B, A ).

% Es una lista ?
list([]).
list([_|_]).

% Es booleano ? true, fail o predicado
bool( true ).
bool( fail ).
bool( X ) :- 
	not(list(X)),
	not(atom(X)),
	callable( X ).

% Convierte predicados a booleanos(true/fail)
% Si ya es booleano, se copia.
bool( X, _ ) :- 
	not(bool(X)), !,
	fail.
bool( X, true ) :- call(X), !.
bool( _, fail ).

% Soporta numeros, atomos, listas, booleanos y predicados.
% Los predicados son evaluados y tratados como booleanos .
equal( X, X ) :- !.
equal( X, Y ) :- 
	list(X),
	list(Y),
	alike( X, Y ), !.
equal( X, Y ) :- 
	bool(X,A),
	bool(Y,B),
	A == B.

% Guarda el resultado de equal(X,Y) en el 3er argumento
equal( X, Y, true ) :- equal( X, Y ), !.
equal( _, _, fail ).

% Imprime una lista de palabras y un salto de linea al final
printl([]) :- nl.
printl([X|R]) :- 
	write( X ),
	write(' '),
	printl(R).


% Textos a mostrar para cada tipo de log
result( true, 'PASS' ).
result( fail, 'FAIL' ).

% Que logs mostrar (comentar para ignorar alguno)
log( fail ).
log( true ).

getPreff( Res, Preff ) :-
	log( Res ),
	result( Res, Preff ).

% Imprime el resultado de una operacion
log( Res, List ) :-
	getPreff( Res, Preff ), !,
	write(Preff), write(': '), printl( List ).
log( _, _ ).

% Chequea si dos elementos son iguales
% e imprime un mensaje descriptivo.
% Los predicados son evaluados y convertidos a booleano.
assertEq( X, Y ) :- 
	equal( X, Y, Res ),
	log( Res, [X,'==',Y] ).
	
% Para relaciones, que devuelven listas
% Arg es el argumento a recolectar
% No importa el orden ni las repeticiones
assertEq( Pred, Arg, List ) :-
	findall( Arg, Pred, List2 ),
	equal( List, List2, Res ),
	log( Res, [Pred,'==',List] ).

% Chequea que X sea true o un predicado verdadero.
assertTrue( X ) :-
	equal( X, true, Res ),
	log( Res, [X, succeeds] ).
	
% Chequea que X sea fail o un predicado falso.
assertFail( X ) :- 
	equal( X, fail, Res ),
	log( Res, [X, fails] ).
	
/*
 Para hacer otros asserts se puede usar la estructura de assertEq/2
 Ejemplo de uso:
 
 tests :-
 	assertTrue( equal(1,1) ),
 	assertFail( equal(1,2) ),
 	assertEq( equal(1,1), true ),
 	assertEq( 1, 1 ),
 	assertEq( equal(1,1), equal(ab,ab) ),
 	assertEq( [1,2,3], [1,2,3] ),
 	assertEq( padre(homero,Hijo), Hijo, [bart,lisa,maggie] ).
 	
 ?- tests.
*/